home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / GUSI / GUSIINET.cp < prev    next >
Text File  |  1993-10-31  |  13KB  |  550 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIINET.cp        -    TCP/IP Sockets, general routines
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by 
  7.     
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.           
  11. Started    :    12Aug92                                Language    :    MPW C/C++
  12. Modified    :    16Aug92    MN Split
  13.                 20Aug92    MN    Wrote most of the functions
  14.                 23Aug92    MN    INETSocket::Available()
  15.                 31Jan93    MN    Inetd support
  16.                 07Feb93    MN    New configuration technique
  17.                 17Jul93    MN    htonl and friends
  18.                 31Oct93    MN    Deferred opening of MacTCP Services
  19. Last        :    31Oct93
  20. *********************************************************************/
  21.  
  22. #include "GUSIINET_P.h"
  23. #include <machine/endian.h>
  24.  
  25. #pragma segment GUSIINET
  26.  
  27. /***************************** Globals ******************************/
  28.  
  29. #define NUM_PBS    32
  30.  
  31. INETSocketDomain    INETSockets;
  32.  
  33. /***************************** Peanuts ******************************/
  34.  
  35. unsigned long (htonl)(unsigned long h)
  36. {
  37.     return h;
  38. }
  39.  
  40. unsigned short    (htons)(unsigned short h)
  41. {
  42.     return h;
  43. }
  44.  
  45. unsigned long (ntohl)(unsigned long n)
  46. {
  47.     return n;
  48. }
  49.  
  50. unsigned short    (ntohs)(unsigned short n)
  51. {
  52.     return n;
  53. }
  54.  
  55. /***************** Our resident MacTCP error expert *****************/
  56.  
  57. /*
  58.  * Convert a MacTCP err code into a unix error code.
  59.  */
  60.  
  61. int TCP_error(int MacTCPerr)
  62. {
  63.     switch ( MacTCPerr ) {
  64.     case 0:
  65.         return 0;
  66.     case ipBadLapErr:
  67.     case ipBadCnfgErr:
  68.     case ipNoCnfgErr:
  69.     case ipLoadErr:
  70.     case ipBadAddr:
  71.         errno = ENXIO;            /* device not configured */    /* a cheap cop out */
  72.         break;
  73.     case connectionClosing:
  74.         errno = ESHUTDOWN;        /* Can't send after socket shutdown */
  75.         break;
  76.     case connectionExists:
  77.         errno = EISCONN;        /* Socket is already connected */
  78.         break;
  79.     case connectionTerminated:
  80.         errno = ENOTCONN;        /* Connection reset by peer */  /* one of many possible */
  81.         break;
  82.     case openFailed:
  83.         errno = ECONNREFUSED;    /* Connection refused */
  84.         break;
  85.     case duplicateSocket:        /* technically, duplicate port */
  86.         errno = EADDRINUSE;        /* Address already in use */
  87.         break;
  88.     case ipDestDeadErr:
  89.         errno = EHOSTDOWN;        /* Host is down */
  90.         break;
  91.     case ipRouteErr:
  92.         errno = EHOSTUNREACH;    /* No route to host */
  93.         break;
  94.     default:
  95.         errno = MacTCPerr > 0 ? MacTCPerr : EFAULT;        /* cop out; an internal err, unix err, or no err */
  96.         break;
  97.     }
  98.  
  99.     return -1;
  100. }
  101.  
  102. /************************ INETD support ************************/
  103.  
  104. pascal OSErr 
  105. TCPPossession(AppleEvent* messagein, AppleEvent* /*reply*/, long /*refIn*/)
  106. {
  107.     AEDesc        streamDesc;
  108.     OSErr            theErr;
  109.     TCPSocket *    sock;
  110.  
  111.     if ((theErr = AEGetParamDesc(messagein, 'STRM', typeLongInteger, &streamDesc)) == noErr) {
  112.         HLock(streamDesc.dataHandle);
  113.         sock = new TCPSocket(*((StreamPtr*) *(streamDesc.dataHandle)));
  114.         HUnlock(streamDesc.dataHandle);
  115.         Sockets.Possess(0, sock);
  116.         Sockets.Possess(1, sock);
  117.         Sockets.Possess(2, sock);
  118.         
  119.         theErr = AEDisposeDesc(&streamDesc);
  120.     }
  121.  
  122.     return theErr;
  123. }
  124.  
  125. pascal OSErr 
  126. UDPPossession(AppleEvent* messagein, AppleEvent* /*reply*/, long /*refIn*/)
  127. {
  128.     AEDesc        streamDesc;
  129.     OSErr            theErr;
  130.     UDPSocket *    sock;
  131.  
  132.     if ((theErr = AEGetParamDesc(messagein, 'STRM', typeLongInteger, &streamDesc)) == noErr) {
  133.         HLock(streamDesc.dataHandle);
  134.         sock = new UDPSocket(*((StreamPtr*) *(streamDesc.dataHandle)));
  135.         HUnlock(streamDesc.dataHandle);
  136.         Sockets.Possess(0, sock);
  137.         Sockets.Possess(1, sock);
  138.         Sockets.Possess(2, sock);
  139.         
  140.         theErr = AEDisposeDesc(&streamDesc);
  141.     }
  142.  
  143.     return theErr;
  144. }
  145.  
  146. /************************ INETSocket members ************************/
  147.  
  148. INETSocket::INETSocket()
  149.     : Socket()
  150. {
  151.     bzero(&sa, sizeof(struct sockaddr_in));
  152.     bzero(&peer, sizeof(struct sockaddr_in));
  153.     
  154.     sa.sin_family    = AF_INET;
  155.     sa.sin_len        = sizeof(struct sockaddr_in);
  156.     status            = SOCK_STATUS_USED;
  157.     nonblocking        = false;    
  158. }
  159.  
  160. INETSocket::INETSocket(StreamPtr stream)
  161.     : Socket(), stream(stream)
  162. {
  163.     sa.sin_family    = AF_INET;
  164.     sa.sin_len        = sizeof(struct sockaddr_in);
  165.     peer.sin_family= AF_INET;
  166.     peer.sin_len    = sizeof(struct sockaddr_in);
  167.     status            = SOCK_STATUS_USED;
  168.     sstate            = SOCK_STATE_CONNECTED;
  169.     nonblocking        = false;    
  170. }
  171.  
  172. INETSocket::~INETSocket()
  173. {
  174. }
  175.  
  176. unsigned long INETSocket::Available()
  177. {
  178.     return 0;
  179. }
  180.  
  181. /*
  182.  *    bind(name, namelen)
  183.  *
  184.  *        bind requests that the name (ip address and port) pointed to by 
  185.  *        name be assigned to the socket.
  186.  *        
  187.  *        The return value is 0 on success or -1 if an error occurs,
  188.  *        in which case global variable errno is set to one of:
  189.  *
  190.  *        EAFNOSUPPORT        The address family in name is not AF_INET.
  191.  *        
  192.  *        EINVAL              The socket is already bound to an address.
  193.  *        
  194.  *        EADDRNOTAVAIL       The specified address is  not  available
  195.  *                             from the local machine. ie. the address
  196.  *                            portion of name was not this machine's address.
  197.  *
  198.  *        MacTCP does not separate name binding and connection establishment.
  199.  *        Therefore the port number is not verified, just stored for later use.
  200.  *
  201.  *        If a specific local port is not required, bind is optional in this
  202.  *        implementation.
  203.  */    
  204.  
  205. int INETSocket::bind(void * addr, int namelen)
  206. {
  207.     struct sockaddr_in *name    =    (struct sockaddr_in *)addr;
  208.         
  209.     if (namelen < sizeof(struct sockaddr_in))
  210.         return GUSI_error(EINVAL);
  211.  
  212.     if (name->sin_family != AF_INET)
  213.         return GUSI_error(EAFNOSUPPORT);
  214.  
  215.     if (sa.sin_port != 0) /* already bound */
  216.         return GUSI_error(EINVAL);
  217.  
  218.     /*
  219.      *    If client passed a local IP address, assure it is the right one
  220.      */
  221.     if (name->sin_addr.s_addr != 0) 
  222.     {
  223.         struct GetAddrParamBlock pbr;
  224.         
  225.         pbr.ioCRefNum     = INETSockets.Driver();
  226.         pbr.csCode         = ipctlGetAddr;
  227.         
  228.         if (PBControlSync(ParmBlkPtr(&pbr)))
  229.             return GUSI_error(ENETDOWN);
  230.         
  231.         if (name->sin_addr.s_addr != pbr.ourAddress)
  232.             return GUSI_error(EADDRNOTAVAIL);
  233.     }
  234.  
  235.     /*
  236.      *    NOTE: can't check a TCP port for EADDRINUSE
  237.      *    just save the address and port away for connect or listen or...
  238.      */
  239.     sa.sin_addr.s_addr     = name->sin_addr.s_addr;
  240.     sa.sin_port             = name->sin_port;
  241.     
  242.     return 0;
  243. }
  244.  
  245. /*
  246.  *    getsockname(name, namelen)
  247.  *
  248.  *        getsockname returns the current name for the  socket.
  249.  *        Namelen should  be initialized to
  250.  *        indicate the amount of space pointed to by name.  On  return
  251.  *        it contains the actual size of the name returned (in bytes).
  252.  *        
  253.  *        A 0 is returned if the call succeeds, -1 if it fails.
  254.  */
  255.  
  256. int INETSocket::getsockname(void *name, int *namelen)
  257. {
  258.     if (*namelen < 0)
  259.         return GUSI_error(EINVAL);
  260.  
  261.     memcpy(name, &sa, min(*namelen, sizeof(struct sockaddr_in)));
  262.     return 0;
  263. }
  264.  
  265. /*
  266.  *    getpeername(name, namelen)
  267.  *
  268.  *        getpeername returns the name of the peer connected to socket.
  269.  *
  270.  *        The  int  pointed  to  by the namelen parameter
  271.  *        should be  initialized  to  indicate  the  amount  of  space
  272.  *        pointed  to  by name.  On return it contains the actual size
  273.  *        of the name returned (in bytes).  The name is  truncated  if
  274.  *        the buffer provided is too small.
  275.  *        
  276.  *        A 0 is returned if the call succeeds, -1 if it fails.
  277.  */
  278.  
  279. int INETSocket::getpeername(void *name, int *namelen)
  280. {
  281.     if (*namelen < 0)
  282.         return GUSI_error(EINVAL);
  283.  
  284.     memcpy(name, &peer, min(*namelen, sizeof(struct sockaddr_in)));
  285.     return 0;
  286. }
  287.  
  288. /*
  289.  *    shutdown(how)
  290.  *
  291.  *        shutdown call causes all or part of a full-duplex
  292.  *        connection on the socket to be shut down.  If
  293.  *        how is 0, then further receives will be disallowed.  If  how
  294.  *        is  1,  then further sends will be disallowed.  If how is 2,
  295.  *        then further sends and receives will be disallowed.
  296.  *        
  297.  *        A 0 is returned if the call succeeds, -1 if it fails.
  298.  */
  299.  
  300. int INETSocket::shutdown(int how)
  301. {
  302.     switch(how) {
  303.     case 0 : 
  304.         status |= SOCK_STATUS_NOREAD;
  305.         break;
  306.     case 1 : 
  307.         status |= SOCK_STATUS_NOWRITE;
  308.         break;
  309.     case 2 :
  310.         status |= SOCK_STATUS_NOREAD | SOCK_STATUS_NOWRITE;
  311.         break;
  312.     default :
  313.         return GUSI_error(EINVAL);
  314.     }
  315.     
  316.     return 0;
  317. }
  318.  
  319. /*
  320.  *    fcntl() operates on the socket according to the order in cmd:
  321.  *
  322.  *        F_GETFL    returns the descriptor status flags. The only
  323.  *                flag supported is FNDELAY for non-blocking i/o.
  324.  *
  325.  *        F_SETFL    sets descriptor status flags. The only
  326.  *                 flag supported is FNDELAY for non-blocking i/o.
  327.  *
  328.  *        Upon successful completion, the value  returned  depends  on
  329.  *        cmd as follows:
  330.  *         F_GETFL   Value of flags.
  331.  *            F_SETFL   0.
  332.  *
  333.  *        On error, a value of -1  is returned and errno is set to indicate 
  334.  *        the error.
  335.  *
  336.  *        EBADF           s is not a valid open descriptor.
  337.  *
  338.  *        EMFILE          cmd is F_DUPFD and socket descriptor table is full.
  339.  *
  340.  *        EINVAL          cmd is F_DUPFD and arg  is  negative  or
  341.  *                      greater   than   the  maximum  allowable
  342.  *                      number (see getdtablesize).
  343.  */
  344. int INETSocket::fcntl(unsigned int cmd, int arg)
  345. {
  346.     switch(cmd) {
  347.     /*
  348.      *  Get socket status.  This is like getsockopt().
  349.      *  Only supported descriptor status is FNDELAY.
  350.      */
  351.     case F_GETFL : 
  352.         if (nonblocking)
  353.             return FNDELAY;
  354.         else
  355.             return 0;
  356.     /*
  357.      *  Set socket status.  This is like setsockopt().
  358.      *  Only supported descriptor status is FNDELAY.
  359.      */
  360.     case F_SETFL : 
  361.         if (arg & FNDELAY)
  362.             nonblocking = true;
  363.         else
  364.             nonblocking = false;
  365.         
  366.         return 0;
  367.     default:
  368.         return GUSI_error(EOPNOTSUPP);
  369.     }
  370. }
  371.  
  372. int INETSocket::ioctl(unsigned int request, void *argp)
  373. {
  374.     struct ifreq *    ifr;
  375.     int                size;
  376.     
  377.     /*
  378.      * Interpret high order word to find amount of data to be copied 
  379.      * to/from the user's address space.
  380.      */
  381.     size =(request &~(IOC_INOUT | IOC_VOID)) >> 16;
  382.     
  383.     /*
  384.      * Zero the buffer on the stack so the user gets back something deterministic.
  385.      */
  386.     if ((request & IOC_OUT) && size)
  387.         bzero((Ptr)argp, size);
  388.  
  389.     ifr =(struct ifreq *)argp;
  390.     switch(request) {
  391.     /* Non-blocking I/O */
  392.     case FIONBIO:
  393.         nonblocking = (Boolean) *(int *) argp;
  394.         return 0;
  395.     /* Number of bytes on input Q */
  396.     case FIONREAD:
  397.         *(unsigned long *) argp    = Available();
  398.         
  399.         return 0;
  400.     default :
  401.         return GUSI_error(EOPNOTSUPP);
  402.     }
  403. }
  404.  
  405. /********************* INETSocketDomain members *********************/
  406.  
  407. extern "C" void GUSIwithInternetSockets()
  408. {
  409.     INETSockets.DontStrip();
  410. }
  411.  
  412. INETSocketDomain::INETSocketDomain()
  413.     :    SocketDomain(AF_INET)    
  414. {
  415.     GUSIConfiguration    conf;        // GUSIConfig isn't yet guaranteed to work    
  416.     
  417.     driverState        = 1;
  418.     resolverState    = 1;
  419.     drvrRefNum         = 0;
  420.     
  421.     /* allocate storage for pbs */
  422.     pbLast = 0;
  423.     pbList = new AnnotatedPB[NUM_PBS];
  424.     
  425.     if (conf.IsDaemon()) {
  426.         if (conf.tcpDaemon)
  427.             AEInstallEventHandler(
  428.                 'INET', 'TSTR', EventHandlerProcPtr(TCPPossession), 0, false);
  429.         else
  430.             AEInstallEventHandler(
  431.                 'INET', 'TSTR', EventHandlerProcPtr(GetOffMyCloud), 0, false);
  432.         
  433.         if (conf.udpDaemon)
  434.             AEInstallEventHandler(
  435.                 'INET', 'USTR', EventHandlerProcPtr(UDPPossession), 0, false);
  436.         else
  437.             AEInstallEventHandler(
  438.                 'INET', 'USTR', EventHandlerProcPtr(GetOffMyCloud), 0, false);
  439.     }
  440. }
  441.  
  442. short INETSocketDomain::Driver()
  443. {
  444.     ParamBlockRec         pb; 
  445.  
  446.     if (driverState == 1) {
  447.         pb.ioParam.ioCompletion    = 0L; 
  448.         pb.ioParam.ioNamePtr     = "\p.IPP"; 
  449.         pb.ioParam.ioPermssn     = fsCurPerm;
  450.         
  451.         driverState                 = PBOpenSync(&pb);
  452.         drvrRefNum                     = pb.ioParam.ioRefNum; 
  453.     }
  454.     
  455.     return driverState ? 0 : drvrRefNum;
  456. }
  457.  
  458. OSErr    INETSocketDomain::Resolver()
  459. {
  460.     if (resolverState == 1)
  461.         resolverState = OpenResolver(nil);
  462.     
  463.     return resolverState;
  464. }
  465.  
  466. /*
  467.  *    INETSocketDomain::socket(type, protocol)
  468.  *
  469.  *        Create a MacTCP socket and return a descriptor.
  470.  *
  471.  *        Type may be SOCK_STREAM to create a TCP socket or 
  472.  *        SOCK_DGRAM to create a UDP socket.
  473.  *                 
  474.  *        Protocol is ignored. (isn't it always?)
  475.  *                 
  476.  *        TCP sockets provide sequenced, reliable, two-way connection
  477.  *        based byte streams.
  478.  *
  479.  *        A TCP socket must be in a connected
  480.  *        state before any data may be sent or received on it. A 
  481.  *        connection to another socket is created with a connect() call
  482.  *        or the listen() and accept() calls.
  483.  *        Once connected, data may be transferred using read() and
  484.  *        write() calls or some variant of the send() and recv()
  485.  *        calls. When a session has been completed a close() may  be
  486.  *        performed.
  487.  *
  488.  *        
  489.  *        A UDP socket supports the exchange of datagrams (connectionless, 
  490.  *        unreliable messages of a fixed maximum length) with  
  491.  *        correspondents named in send() calls. Datagrams are
  492.  *        generally received with recv(), which returns the next
  493.  *        datagram with its return address.
  494.  *
  495.  *        An fcntl() or ioctl() call can be used to enable non-blocking I/O.
  496.  *
  497.  *        The return value is a descriptor referencing the socket or -1
  498.  *        if an error occurs, in which case global variable errno is
  499.  *        set to one of:
  500.  *
  501.  *            ENOMEM                    Failed to allocate memory for the socket
  502.  *                              data structures.
  503.  *
  504.  *            ESOCKTNOSUPPORT     Type wasn't SOCK_STREAM or SOCK_DGRAM.
  505.  *
  506.  *            EMFILE              The socket descriptor table is full.
  507.  */
  508.  
  509. Socket * INETSocketDomain::socket(int type, short)
  510. {
  511.     INETSocket * sock    =    nil;
  512.     
  513.     errno    =    0;
  514.  
  515.     if (!Driver())
  516.         return (Socket *) GUSI_error_nil(ENETDOWN);
  517.         
  518.     switch (type)    {
  519.     case SOCK_STREAM:
  520.         sock = new TCPSocket();
  521.         break;
  522.     case SOCK_DGRAM:
  523.         sock = new UDPSocket();
  524.         break;
  525.     default:
  526.         GUSI_error(ESOCKTNOSUPPORT);
  527.     }    
  528.     
  529.     if (sock && errno)    {
  530.         delete sock;
  531.         
  532.         return nil;
  533.     } else
  534.         return sock;
  535. }
  536.  
  537. AnnotatedPB * INETSocketDomain::GetPB()
  538. {
  539.     AnnotatedPB    *    curPB;
  540.     
  541.     do {
  542.         if (++pbLast == NUM_PBS)
  543.             pbLast = 0;
  544.         
  545.         curPB = pbList + pbLast;
  546.     } while (curPB->Busy());
  547.     
  548.     return curPB;
  549. }
  550.